<template>

	<el-form ref="formRef" :disabled="isVisitor()" :model="user" :rules="rules" label-width="auto">
		<el-form-item label="用户名" prop="username">
			<el-tooltip
					:content="`注意：用户名180天内只能修改一次，上次修改时间为：${userStore.lastModifyUsernameDate ? formatDateTime(userStore.lastModifyUsernameDate) : '无'}`"
					:disabled="isVisitor()"
					:hide-after="0"
					:show-after="200"
					placement="top-start">
				<el-input v-model.trim="user.username" clearable/>
			</el-tooltip>
		</el-form-item>
		<el-form-item label="昵称" prop="nickname">
			<el-tooltip
					:content="`注意：昵称30天内只能修改一次，上次修改时间为：${userStore.lastModifyNicknameDate ? formatDateTime(userStore.lastModifyNicknameDate) : '无'}`"
					:disabled="isVisitor()"
					:hide-after="0"
					:show-after="200"
					placement="top-start">
				<el-input v-model.trim="user.nickname" clearable/>
			</el-tooltip>
		</el-form-item>
		<el-form-item label="性别" prop="gender">
			<business-code-radio v-model="user.gender" :cancel-able="false" border code-type="gender"/>
		</el-form-item>
		<el-form-item label="邮箱" prop="email">
			<el-input v-model.trim="user.email" clearable></el-input>
		</el-form-item>
		<el-form-item v-if="userStore.email && mailBoxStatus !== 1">
			<template #label></template>
			<div class="cus-start" style="width: 100%">
				<el-text type="danger">邮箱未激活，请查收相关邮件。</el-text>
				<el-text type="warning">（如果您已激活，请刷新页面）</el-text>
				<div v-if="mailBoxStatus === 0" style="margin-left: auto">
					<el-text>未收到邮件或链接已失效？您可以：</el-text>
					<el-button :loading="loading" size="small" @click="sendEmail">重新发送</el-button>
				</div>
			</div>
		</el-form-item>
		<el-form-item label="头像" prop="avatar">
			<el-upload
					ref="uploadRef"
					:auto-upload="false"
					:file-list="avatarFileList"
					:http-request="httpRequest"
					:on-change="fileListChanged"
					:on-error="onError"
					:on-preview="onImagePreview"
					:on-remove="fileRemoved"
					:on-success="onUploadSuccess"
					accept=".jpg,.jpeg,.png"
					drag
					list-type="picture-card"
			>
				<template #trigger>
					<em>点击或将文件拖拽至此处</em>
				</template>
				<template #tip>
					<div>
						支持 jpg/jpeg/png 格式，文件大小不超过 10MB
					</div>
				</template>
			</el-upload>
		</el-form-item>
		<el-form-item label="个人简介" prop="introduction">
			<el-input v-model="user.introduction" :autosize="{ minRows: 4, maxRows: 4}" :placeholder="DEFAULT_USER.DEFAULT_INTRODUCTION"
			          :resize="isVisitor() ? 'none' : 'vertical'" clearable
			          maxlength="500" type="textarea"/>
		</el-form-item>
		<el-form-item v-if="!isVisitor()">
			<template #label></template>
			<el-button :loading="loading" icon="el-icon-CircleCheck" type="primary" @click="updateMyself">保存</el-button>
			<el-button :loading="loading" icon="el-icon-Refresh" type="primary" @click="resetMyself">重置</el-button>
		</el-form-item>
	</el-form>

	<!-- 头像大图预览 -->
	<el-image-viewer v-if="previewDialogVisible" :url-list="[previewImageUrl]" hide-on-click-modal @close="previewDialogVisible = false"/>

	<!-- 图片裁剪 -->
	<cropper
			ref="clipperRef"
			v-model:visible="cropperVisible"
			:allow-type-list="clipperData.allowTypeList"
			:fixed-number="clipperData.fixedNumber"
			:fixed-number-aider="clipperData.fixedNumberAider"
			:img="avatarImg"
			:limit-size="clipperData.limitSize"
			:preview-width="clipperData.previewWidth"
			:type="clipperData.type"
			file-name="avatar.png"
			title="头像裁剪"
			@cancel="onCropperCancel"
			@confirm="onCropperConfirm"/>

</template>

<script lang="ts" setup>

import {getEmailStatusByUserIdApi, sendActivateEmailApi, updateMyselfApi} from '@/api/system/myself.api'
import {useUserStore} from '@/stores/modules/user.store'
import {onBeforeUnmount, reactive, ref, toRaw} from 'vue'
import type {FormInstance, FormRules, UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile} from 'element-plus'
import {focusFormErrorElement, formatDateTime, handleAvatarUrl, messageError, messageSuccess, messageWarning, usernameValidator} from '@/utils/commonUtil'
import type {UserVo} from '@/model/user-import'
import {isVisitor} from '@/utils/authUtil'
import {DEFAULT_USER} from '@/utils/constantsUtil'
import BusinessCodeRadio from '@/components/business-code-radio.vue'
import {useOssStore} from '@/stores/modules/oss.store'
import Cropper from '@/components/cropper.vue'
import type {CusUploadRawFile} from '@/model/global-import'

const userStore = useUserStore()

const formRef = ref<FormInstance>()

const loading = ref(false)
const mailBoxStatus = ref<MailBoxStatus>(1)

const user = reactive<Partial<UserVo>>({
	nickname: toRaw(userStore.nickname),
	username: toRaw(userStore.username),
	email: toRaw(userStore.email),
	avatar: toRaw(userStore.avatar),
	gender: toRaw(userStore.gender),
	introduction: toRaw(userStore.introduction)
})

const rules = reactive<FormRules>({
	nickname: [
		{required: true, message: '请输入昵称', trigger: 'change'},
		{min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'change'}
	],
	username: [
		{required: true, message: '请输入用户名', trigger: 'change'},
		{message: '①可以包英文含字母和数字 ②字母开头 ③长度为2-20', trigger: 'change', validator: usernameValidator}
	],
	introduction: [
		{required: false, max: 500, message: '长度不超过 500 个字符', trigger: 'change'}
	],
	email: [
		{max: 320, message: '长度不超过 320 个字符', trigger: 'change'},
		{type: 'email', message: '邮箱格式不正确', trigger: 'change'}
	],
	avatar: [
		{required: false, max: 500, message: '500 个字符以内', trigger: 'change'}
	],
	gender: [
		{required: true, message: '请选择性别', trigger: 'change'}
	]
})

const updateMyself = () => {
	formRef.value?.validate(isValid => {
		if (isValid) {
			loading.value = true
			if (avatarFileList.value[0] && avatarFileList.value[0].status !== 'success') {
				// 上传头像，触发 httpRequest() 方法
				uploadRef.value?.submit()
			} else {
				// 不上传头像，直接更新个人信息
				doUpdateMyself()
			}
		} else {
			focusFormErrorElement()
		}
	})
}

const doUpdateMyself = () => {
	const data: UpdateMyselfRequest = {
		userId: userStore.userId!,
		nickname: user.nickname!,
		username: user.username!,
		email: user.email,
		gender: user.gender,
		avatar: user.avatar,
		introduction: user.introduction
	}
	updateMyselfApi(data).then(response => {
		const newUser = response.data?.user
		// 如果修改了邮箱，则重新获取邮箱状态
		if (newUser?.email !== userStore.email) {
			getMailBoxStatus()
		}
		userStore.modifyUserInfo(newUser)
		// 如果修改了用户名或昵称，则需要更新token
		userStore.modifyToken(response.data?.token, response.data?.expire)
		messageSuccess('保存成功')
	}).finally(() => {
		loading.value = false
	})
}

const resetMyself = () => {
	user.nickname = toRaw(userStore.nickname)
	user.username = toRaw(userStore.username)
	user.email = toRaw(userStore.email)
	user.gender = toRaw(userStore.gender)
	user.avatar = toRaw(userStore.avatar)
	user.introduction = toRaw(userStore.introduction)
	resetAvatar()
}

const resetAvatar = () => {
	if (userStore.avatar) {
		if (avatarFileList.value[0]?.status !== 'success') {
			avatarFileList.value = []
			avatarFileList.value.push({
				url: handleAvatarUrl(userStore.avatar),
				name: '',
				status: 'success',
				uid: 0
			})
		}
	} else {
		avatarFileList.value = []
	}
}

const sendEmail = () => {
	loading.value = true
	sendActivateEmailApi({userId: userStore.userId!, email: userStore.email!}).then(() => {
		messageSuccess('邮件已发送')
	}).finally(() => {
		loading.value = false
	})
}

const getMailBoxStatus = () => {
	getEmailStatusByUserIdApi(userStore.userId!).then(response => {
		const mailBoxStatusResponse = response.data
		mailBoxStatus.value = mailBoxStatusResponse.status
		if (userStore.email !== mailBoxStatusResponse.email) {
			userStore.email = mailBoxStatusResponse.email
			user.email = mailBoxStatusResponse.email
		}
	})
}

// 用户头像上传 - 开始

const ossStore = useOssStore()

const uploadRef = ref<UploadInstance>()

const avatarFileList = ref<UploadFiles>([])
const avatarImg = ref('')
const cropperVisible = ref(false)
const clipperData = ref<IClipper>({
	type: '',
	allowTypeList: ['jpg', 'png'],
	limitSize: 1,
	fixedNumber: [1, 1],
	previewWidth: 0
})
const previewImageUrl = ref('')
const previewDialogVisible = ref(false)

/**
 * 文件状态改变时的钩子，添加文件、上传成功、上传失败时都会被调用
 */
const fileListChanged: UploadProps['onChange'] = (file, fileList) => {
	// 如果是上传成功后触发，则不再处理
	if (file.status === 'success') {
		return
	}
	// 校验文件格式和大小
	if (!file.name.endsWith('.jpg') && !file.name.endsWith('.jpeg') && !file.name.endsWith('.png') || file.size! / 1024 / 1024 > 10) {
		messageWarning('头像支持jpg/jpeg/png格式，大小不超过10m')
		fileList.pop()
		return
	}
	// 校验文件数量，替换后仅保留最后一次选择的文件
	if (fileList.length > 1) {
		fileList.splice(0, 1)
	}
	avatarFileList.value = fileList
	if (avatarFileList.value && avatarFileList.value.length > 0) {
		user.avatar = avatarFileList.value[0].name
	} else {
		user.avatar = undefined
	}
	formRef.value?.validateField('avatar')
	// 打开图片剪裁对话框
	if (file.status === 'ready' && !(<CusUploadRawFile>file.raw)!.avatarCropped) {
		avatarImg.value = file.url!
		cropperVisible.value = true
	}
}

/**
 * 文件列表移除文件时的钩子
 */
const fileRemoved: UploadProps['onRemove'] = (file, fileList) => {
	avatarFileList.value = fileList
	if (avatarFileList.value && avatarFileList.value.length > 0) {
		user.avatar = avatarFileList.value[0].name
	} else {
		user.avatar = undefined
	}
	formRef.value?.validateField('avatar')
}

const onImagePreview: UploadProps['onPreview'] = uploadFile => {
	previewImageUrl.value = uploadFile.url!
	previewDialogVisible.value = true
}

// 用于存储创建的对象URL，方便后续释放
const objectUrlToRevoke = ref<string>()

const onCropperConfirm = (file: File) => {
	avatarFileList.value[0].name = file.name
	avatarFileList.value[0].size = file.size

	const URL = window.URL || window.webkitURL
	// 如果之前有创建的URL，先释放
	if (objectUrlToRevoke.value) {
		URL.revokeObjectURL(objectUrlToRevoke.value)
	}

	// 创建新的URL
	const objectUrl = URL.createObjectURL(file)
	avatarFileList.value[0].url = objectUrl
	// 保存创建的URL以便后续释放
	objectUrlToRevoke.value = objectUrl

	const uploadRawFile: UploadRawFile = <UploadRawFile>file
	uploadRawFile.uid = avatarFileList.value[0].uid
	avatarFileList.value[0].raw = uploadRawFile
	const cusUploadRawFile: CusUploadRawFile = avatarFileList.value[0].raw!
	cusUploadRawFile.avatarCropped = true
	uploadRef.value?.handleStart(avatarFileList.value[0].raw!)
}

const onCropperCancel = () => {
	user.avatar = undefined
	clearAvatarFiles()
	formRef.value?.validateField('avatar')
}

const clearAvatarFiles = () => {
	avatarFileList.value = []
	uploadRef.value?.clearFiles()
}

/**
 * 覆盖默认的上传行为，可以自定义上传的实现
 * 文件上传并提交视频任务
 */
const httpRequest: UploadProps['httpRequest'] = async ({file}) => {
	// 检查ossClient是否初始化，没有则进行初始化
	await ossStore.initOssClient()
	// 定义唯一的文件名（含路径），示例：avatar/username/1672917110368/头像.jpg
	const filePath = `avatar/${user.username}/${new Date().getTime()}/${file.name}`
	const {res, name} = await ossStore.ossClient!.put(filePath, file)
	if (res.status === 200) {
		// name是oss上的文件存储路径，开头没有斜杠，不含bucket名称
		user.avatar = name
		return Promise.resolve()
	} else {
		messageError('头像上传失败')
		return Promise.reject()
	}
}

/**
 * 文件上传成功时的钩子<br/>
 * 执行注册操作
 */
const onUploadSuccess = (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
	doUpdateMyself()
}

/**
 * 文件上传失败时的钩子，处理httpRequest()方法内未捕获的异常
 */
const onError: UploadProps['onError'] = (err, file, fileList) => {
	messageError(err.message)
	loading.value = false
}

// 用户头像上传 - 结束

getMailBoxStatus()
resetAvatar()

onBeforeUnmount(() => {
	// 在组件卸载前释放所有创建的对象URL
	if (objectUrlToRevoke.value) {
		const URL = window.URL || window.webkitURL
		URL.revokeObjectURL(objectUrlToRevoke.value)
		objectUrlToRevoke.value = undefined
	}
})

</script>
